home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / WWW / Perl_WWW_Utilities / weblog1.1 / uncgi / uncgi.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-25  |  8.4 KB  |  422 lines

  1. /*
  2.  * @(#)uncgi.c    1.7 10/19/94
  3.  *
  4.  * Unescape all the fields in a form and stick them in the environment
  5.  * so they can be used without awful machinations.
  6.  *
  7.  * Call with an ACTION such as:
  8.  *    http://foo.bar.com/cgi-bin/uncgi/myscript/extra/path/stuff
  9.  *
  10.  * Uncgi will run "myscript" from the cgi-bin directory, and set PATH_INFO
  11.  * to "/extra/path/stuff".
  12.  *
  13.  * Environment variable names are "WWW_" plus the field name.
  14.  *
  15.  * Copyright 1994, Steven Grimm <koreth@hyperion.com>.
  16.  *
  17.  * Permission is granted to redistribute freely and use for any purpose,
  18.  * commercial or private, so long as this copyright notice is retained
  19.  * and the source code is included free of charge with any binary
  20.  * distributions.
  21.  */
  22. #include <stdio.h>
  23. #include <ctype.h>
  24.  
  25. void *malloc(), *realloc();
  26. char *getenv();
  27.  
  28. extern char *sys_errlist[];
  29. extern int errno;
  30.  
  31. #define PREFIX    "WWW_"
  32. #define ishex(x) (((x) >= '0' && (x) <= '9') || ((x) >= 'a' && (x) <= 'f') || \
  33.           ((x) >= 'A' && (x) <= 'F'))
  34.  
  35. /*
  36.  * Convert two hex digits to a value.
  37.  */
  38. static int
  39. htoi(s)
  40.     unsigned char    *s;
  41. {
  42.     int    value;
  43.     char    c;
  44.  
  45.     c = s[0];
  46.     if (isupper(c))
  47.         c = tolower(c);
  48.     value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;
  49.  
  50.     c = s[1];
  51.     if (isupper(c))
  52.         c = tolower(c);
  53.     value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;
  54.  
  55.     return (value);
  56. }
  57.  
  58. /*
  59.  * Get rid of all the URL escaping in a string.  Modify it in place, since
  60.  * the result will always be equal in length or smaller.
  61.  */
  62. static void
  63. url_unescape(str)
  64.     unsigned char    *str;
  65. {
  66.     unsigned char    *dest = str;
  67.  
  68.     while (str[0])
  69.     {
  70.         if (str[0] == '+')
  71.             dest[0] = ' ';
  72.         else if (str[0] == '%' && ishex(str[1]) && ishex(str[2]))
  73.         {
  74.             dest[0] = (unsigned char) htoi(str + 1);
  75.             str += 2;
  76.         }
  77.         else
  78.             dest[0] = str[0];
  79.  
  80.         str++;
  81.         dest++;
  82.     }
  83.  
  84.     dest[0] = '\0';
  85. }
  86.  
  87. /*
  88.  * Print the start of an error message.
  89.  */
  90. static void
  91. http_head()
  92. {
  93.     puts("Content-type: text/html\n");
  94.     puts("<html><head><title>Error!</title></head><body>");
  95.     puts("<h1>An error has occurred while processing your request.</h1>");
  96. }
  97.  
  98. /*
  99.  * Print an HTML error string with the right HTTP header and exit.
  100.  */
  101. static void
  102. html_perror(str)
  103.     char    *str;
  104. {
  105.     http_head();
  106.  
  107.     puts("<p>The following error was encountered while processing");
  108.     puts("your query.");
  109.     puts("<blockquote>");
  110.     printf("%s: %s\n", str, sys_errlist[errno]);
  111.     puts("</blockquote>");
  112.     puts("<p>Please try again later.");
  113.     puts("</body></html>");
  114.  
  115.     exit(1);
  116. }
  117.  
  118. /*
  119.  * Stuff a URL-unescaped variable, with the prefix on its name, into the
  120.  * environment.  Uses the "=" from the CGI arguments.  Putting an "=" in
  121.  * a field name is probably a bad idea.
  122.  *
  123.  * If the variable is already defined, append a '#' to it along with the
  124.  * new value.
  125.  */
  126. static void
  127. stuffenv(var)
  128.     char    *var;
  129. {
  130.     char    *buf, *c, *oldval, *newval;
  131.  
  132.     url_unescape(var);
  133.  
  134.     /*
  135.      * Allocate enough memory for the variable name and its value.
  136.      */
  137.     buf = malloc(strlen(var) + sizeof(PREFIX) + 1);
  138.     if (buf == NULL)
  139.         html_perror("stuffenv");
  140.  
  141.     strcpy(buf, PREFIX);
  142.     strcpy(buf + sizeof(PREFIX) - 1, var);        /* save a few cycles */
  143.  
  144.     /*
  145.      * If, for some reason, there wasn't an = in the query string,
  146.      * add one so the environment will be valid.
  147.      */
  148.     for (c = buf; *c != '\0'; c++)
  149.         if (*c == '=')
  150.             break;
  151.     if (*c == '\0')
  152.         c[1] = '\0';
  153.     
  154.     /*
  155.      * Check for the presence of the variable.
  156.      */
  157.     *c = '\0';
  158.     if (oldval = getenv(buf))
  159.     {
  160.         newval = malloc(strlen(oldval) + strlen(buf) + strlen(c+1) + 2);
  161.         if (newval == NULL)
  162.             html_perror("stuffenv: append");
  163.         
  164.         *c = '=';
  165.         strcpy(newval, buf);
  166.         strcat(newval, "#");
  167.         strcat(newval, oldval);
  168.         free(buf);
  169.     }
  170.     else
  171.     {
  172.         *c = '=';
  173.         newval = buf;
  174.     }
  175.  
  176.     putenv(newval);
  177. }
  178.  
  179. /*
  180.  * Scan a query string, stuffing variables into the environment.  This
  181.  * should ideally just use strtok(), but that's not available everywhere.
  182.  */
  183. static void
  184. scanquery(q)
  185.     char    *q;
  186. {
  187.     char    *next = q;
  188.  
  189.     do {
  190.         while (*next && *next != '&')
  191.             next++;
  192.         if (! *next)
  193.             next = NULL;
  194.         else
  195.             *next = '\0';
  196.         
  197.         stuffenv(q);
  198.         if (next)
  199.             *next++ = '&';
  200.         q = next;
  201.     } while (q != NULL);
  202. }
  203.  
  204. /*
  205.  * Read a POST query from standard input into a dynamic buffer.  Terminate
  206.  * it with a null character.
  207.  */
  208. static char *
  209. postread()
  210. {
  211.     char    *buf = NULL;
  212.     int    size = 0, sofar = 0, got;
  213.  
  214.     buf = getenv("CONTENT_TYPE");
  215.     if (buf == NULL || strcmp(buf, "application/x-www-form-urlencoded"))
  216.     {
  217.         http_head();
  218.         puts("<p>No content type was passed to uncgi.</body></html>");
  219.         exit(1);
  220.     }
  221.  
  222.     buf = getenv("CONTENT_LENGTH");
  223.     if (buf == NULL)
  224.     {
  225.         http_head();
  226.         puts("<p>The server did not tell uncgi how long the request");
  227.         puts("was.</body></html>");
  228.         exit(1);
  229.     }
  230.     
  231.     size = atoi(buf);
  232.     buf = malloc(size);
  233.     if (buf == NULL)
  234.         html_perror("postread");
  235.     do
  236.     {
  237.         got = fread(buf + sofar, 1, size - sofar, stdin);
  238.         sofar += got;
  239.     } while (got && sofar < size);
  240.  
  241.     buf[sofar] = '\0';
  242.  
  243.     return (buf);
  244. }
  245.  
  246. /*
  247.  * Run a shell script.  We use this instead of the OS's "#!" mechanism
  248.  * because that mechanism doesn't work too well on SVR3-based systems.
  249.  */
  250. void
  251. runscript(shell, script)
  252.     char    *shell, *script;
  253. {
  254.     char    *argvec[4], *space;
  255.     int    pos;
  256.  
  257.     if (shell[0] != '/')
  258.         return;
  259.     
  260.     pos = strlen(shell) - 1;
  261.     if (shell[pos] == '\n')
  262.         shell[pos] = '\0';
  263.  
  264.     argvec[0] = shell;
  265.  
  266.     /*
  267.      * See if there's an argument string.  strchr() isn't available
  268.      * everywhere, so do it ourselves.
  269.      */
  270.     for (space = shell; *space; space++)
  271.         if (*space == ' ')
  272.             break;
  273.  
  274.     if (*space)
  275.     {
  276.         *space = '\0';
  277.         argvec[1] = space + 1;
  278.         argvec[2] = script;
  279.         argvec[3] = NULL;
  280.     }
  281.     else
  282.     {
  283.         argvec[1] = script;
  284.         argvec[2] = NULL;
  285.     }
  286.  
  287.     execv(shell, argvec);
  288.     /* Fall back to main() on error. */
  289. }
  290.  
  291. /*
  292.  * Main program, optionally callable as a library function.
  293.  */
  294. void
  295. #ifdef NO_MAIN
  296. uncgi()
  297. #else
  298. main(argc, argv)
  299.     int    argc;
  300.     char    **argv;
  301. #endif
  302. {
  303.     char    *query, *program, *pathinfo, *newpathinfo, *method;
  304.     char    *ptrans, *ptend;
  305.     char    *argvec[2], shellname[200];
  306.     int    proglen;
  307.     FILE    *fp;
  308.  
  309.     /*
  310.      * First, get the query string, wherever it is, and stick its
  311.      * component parts into the environment.  Allow combination
  312.      * GET and POST queries, even though that's a bit strange.
  313.      */
  314.     query = getenv("QUERY_STRING");
  315.     if (query != NULL && strlen(query))
  316.         scanquery(query);
  317.  
  318.     method = getenv("REQUEST_METHOD");
  319.     if (method != NULL && ! strcmp(method, "POST"))
  320.     {
  321.         query = postread();
  322.         if (query[0] != '\0')
  323.             scanquery(query);
  324.     }
  325.  
  326.     if (query == NULL)
  327.     {
  328.         http_head();
  329.         puts("<p>The 'uncgi' program couldn't find a query to");
  330.         puts("process.\n</body></html>");
  331.         exit(1);
  332.     }
  333.  
  334. #ifndef NO_MAIN
  335.     /*
  336.      * Now figure out which program the caller *really* wants, and adjust
  337.      * PATH_INFO and PATH_TRANSLATED to look right to that program.
  338.      */
  339.     pathinfo = getenv("PATH_INFO");
  340.     if (pathinfo != NULL && pathinfo[0])
  341.     {
  342.         proglen = 1;    /* path_info always starts with '/' */
  343.  
  344.         while (pathinfo[proglen] && pathinfo[proglen] != '/')
  345.             proglen++;
  346.  
  347.         program = malloc(proglen + sizeof(CGI_BIN));
  348.         if (program == NULL)
  349.             html_perror("program");
  350.         strcpy(program, CGI_BIN);
  351.         strncat(program + sizeof(CGI_BIN) - 1, pathinfo, proglen);
  352.  
  353.         /*
  354.          * Strip "program" from PATH_TRANSLATED.
  355.          * XXX - this depends on strcpy() copying front to back.
  356.          */
  357.         ptrans = getenv("PATH_TRANSLATED");
  358.         if (ptrans != NULL)
  359.         {
  360.             ptend = ptrans + strlen(ptrans) - strlen(pathinfo);
  361.             strcpy(ptend, ptend + proglen);
  362.             ptend = malloc(strlen(ptrans) +
  363.                     sizeof("PATH_TRANSLATED="));
  364.             strcpy(ptend, "PATH_TRANSLATED=");
  365.             strcat(ptend, ptrans);
  366.             putenv(ptend);
  367.         }
  368.  
  369.         pathinfo += proglen;
  370.         newpathinfo = malloc(strlen(pathinfo) + sizeof("PATH_INFO="));
  371.         if (newpathinfo == NULL)
  372.             html_perror("newpathinfo");
  373.         strcpy(newpathinfo, "PATH_INFO=");
  374.         strcat(newpathinfo, pathinfo);
  375.         putenv(newpathinfo);
  376.  
  377.  
  378.     }
  379.     else
  380.     {
  381.         /*
  382.          * No PATH_INFO means no program to run.
  383.          */
  384.         http_head();
  385.         puts("<p>Whoever wrote this form doesn't know how to use");
  386.         puts("the 'uncgi' program, because they didn't tell it");
  387.         puts("what to run.");
  388.         puts("<h5>(Bummer.)</h5></body></html>");
  389.  
  390.         exit(0);
  391.     }
  392.  
  393.     /*
  394.      * SVR3-based systems seem to have trouble running shell scripts.
  395.      * So if that's what this is, run its shell explicitly.
  396.      */
  397.     fp = fopen(program, "r");
  398.     if (fp != NULL)
  399.     {
  400.         if (fgets(shellname, sizeof(shellname), fp) != NULL)
  401.         {
  402.             fclose(fp);
  403.             if (shellname[0] == '#' && shellname[1] == '!')
  404.                 runscript(shellname + 2, program);
  405.         }
  406.         fclose(fp);
  407.     }
  408.  
  409.     /*
  410.      * Now execute the program.
  411.      */
  412.     argvec[0] = program;
  413.     argvec[1] = NULL;
  414.     execv(program, argvec);
  415.  
  416.     /*
  417.      * If we get here, the exec failed.
  418.      */
  419.     html_perror(program);
  420. #endif
  421. }
  422.